{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2cc2ebc0-b224-44c7-9f16-6bf72622760b",
   "metadata": {},
   "source": [
    "- https://pytorch.org/blog/overview-of-pytorch-autograd-engine/\n",
    "- 深度学习框架本质上是可微分（可反向传播）的计算图（computation graph）\n",
    "    - 有向无环图（DAG）： 深度学习的计算图通常是一个有向无环图（Directed Acyclic Graph，DAG）。节点代表操作（operation）或变量（variable），边代表数据流（张量流动）。由于图是无环的，数据沿着计算路径单向流动，确保了数据前向传播的顺序性，并且可以反向传播计算梯度。\n",
    "    - 自动求导机制（自动微分）： 计算图的一个核心特点是支持自动求导。反向传播通过链式法则对图中的每个操作自动计算梯度，简化了模型训练中的梯度计算。框架会根据计算图结构，自动生成反向传播的路径。\n",
    "        - chain rule\n",
    "- 所有的深度学习网络所表示的函数（$f(x)$），是一个可微分的函数；\n",
    "    - 不只是 forward，还包括 model forward + loss 整体都是可微的；\n",
    "- 注意 torch 中的一些特殊算子\n",
    "    - 不可微：torch.argmax\n",
    "    - 看似不可微，其实实现 grad_fn 的：topk"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "2f1dd38c-a4c0-4983-9dfb-2fc4c9cfa6cb",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:33:28.053357Z",
     "iopub.status.busy": "2024-10-19T01:33:28.052718Z",
     "iopub.status.idle": "2024-10-19T01:33:29.630811Z",
     "shell.execute_reply": "2024-10-19T01:33:29.630009Z",
     "shell.execute_reply.started": "2024-10-19T01:33:28.053309Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<torch._C.Generator at 0x7bff262a79d0>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "from IPython.display import Image\n",
    "torch.manual_seed(42)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6ba23cd2-a083-4671-b5bc-18c0a0798fff",
   "metadata": {},
   "source": [
    "### 可微分计算图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a5c4dfc0-25f8-4025-91e0-d7390cff41dd",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:33:35.708844Z",
     "iopub.status.busy": "2024-10-19T01:33:35.708503Z",
     "iopub.status.idle": "2024-10-19T01:33:35.717993Z",
     "shell.execute_reply": "2024-10-19T01:33:35.716334Z",
     "shell.execute_reply.started": "2024-10-19T01:33:35.708823Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://pytorch.org/assets/images/chain_rule_backward_differentiation.png\" width=\"400\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url='https://pytorch.org/assets/images/chain_rule_backward_differentiation.png', width=400)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "e7a4805c-c3ba-42f9-b4e4-30d88c371991",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:52:13.253970Z",
     "iopub.status.busy": "2024-10-17T14:52:13.253329Z",
     "iopub.status.idle": "2024-10-17T14:52:13.270224Z",
     "shell.execute_reply": "2024-10-17T14:52:13.268064Z",
     "shell.execute_reply.started": "2024-10-17T14:52:13.253923Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(120., grad_fn=<MulBackward0>)"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.tensor(2.0, requires_grad=True)\n",
    "\n",
    "v = x * 3  # First operation\n",
    "u = v * 4  # Second operation\n",
    "y = u * 5  # Third operation (final result)\n",
    "\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "af45063b-c265-403c-aeb9-1a0963c2d956",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:52:19.362259Z",
     "iopub.status.busy": "2024-10-17T14:52:19.361617Z",
     "iopub.status.idle": "2024-10-17T14:52:19.372467Z",
     "shell.execute_reply": "2024-10-17T14:52:19.370348Z",
     "shell.execute_reply.started": "2024-10-17T14:52:19.362213Z"
    }
   },
   "outputs": [],
   "source": [
    "y.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "f6ee5f1b-cfed-4178-8f05-cc6624d44aa9",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:52:21.797313Z",
     "iopub.status.busy": "2024-10-17T14:52:21.796718Z",
     "iopub.status.idle": "2024-10-17T14:52:21.810608Z",
     "shell.execute_reply": "2024-10-17T14:52:21.808528Z",
     "shell.execute_reply.started": "2024-10-17T14:52:21.797269Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(60.)"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "1b32d582-5e67-48b7-a2c6-682d915989ca",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:52:33.362296Z",
     "iopub.status.busy": "2024-10-17T14:52:33.361646Z",
     "iopub.status.idle": "2024-10-17T14:52:33.371968Z",
     "shell.execute_reply": "2024-10-17T14:52:33.369837Z",
     "shell.execute_reply.started": "2024-10-17T14:52:33.362249Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_3559964/3542285320.py:1: UserWarning: The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If you indeed want the .grad field to be populated for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor instead. See github.com/pytorch/pytorch/pull/30531 for more informations. (Triggered internally at aten/src/ATen/core/TensorBody.h:489.)\n",
      "  u.grad\n"
     ]
    }
   ],
   "source": [
    "u.grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "d87e3e49-5e60-40aa-9c12-c5f35d45d93a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:55:19.865404Z",
     "iopub.status.busy": "2024-10-17T14:55:19.864770Z",
     "iopub.status.idle": "2024-10-17T14:55:19.877500Z",
     "shell.execute_reply": "2024-10-17T14:55:19.875096Z",
     "shell.execute_reply.started": "2024-10-17T14:55:19.865358Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor(2.0, requires_grad=True)\n",
    "\n",
    "v = x * 3  \n",
    "v.retain_grad()\n",
    "u = v * 4  \n",
    "u.retain_grad()\n",
    "y = u * 5  \n",
    "\n",
    "y.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "b3cc8c56-72b6-42f9-9fc2-70e2c1e23850",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:55:21.654473Z",
     "iopub.status.busy": "2024-10-17T14:55:21.653875Z",
     "iopub.status.idle": "2024-10-17T14:55:21.667560Z",
     "shell.execute_reply": "2024-10-17T14:55:21.665478Z",
     "shell.execute_reply.started": "2024-10-17T14:55:21.654428Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(5.)"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# partial y/ partial u\n",
    "u.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e838489-38f0-4a1e-86cc-9f64fc6ba9c1",
   "metadata": {},
   "source": [
    "$$\n",
    "\\frac{\\partial y}{\\partial v}=\\frac{\\partial y}{\\partial u}\\cdot\\frac{\\partial u}{\\partial v}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "22835125-b7c3-449f-b8c8-5a56e9b1b92d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:57:02.560256Z",
     "iopub.status.busy": "2024-10-17T14:57:02.559598Z",
     "iopub.status.idle": "2024-10-17T14:57:02.574507Z",
     "shell.execute_reply": "2024-10-17T14:57:02.572376Z",
     "shell.execute_reply.started": "2024-10-17T14:57:02.560209Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(20.)"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# partial y/partial v = partial y/partial u * partial u/partial v\n",
    "v.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29dbd73a-8d6d-48ab-829d-45196af61671",
   "metadata": {},
   "source": [
    "$$\n",
    "\\frac{\\partial y}{\\partial x}=\\frac{\\partial y}{\\partial u}\\cdot\\frac{\\partial u}{\\partial v}\\cdot\\frac{\\partial v}{\\partial x}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "794ca3c8-f1b9-4be2-a362-298399fb5845",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:55:53.429348Z",
     "iopub.status.busy": "2024-10-17T14:55:53.428701Z",
     "iopub.status.idle": "2024-10-17T14:55:53.442836Z",
     "shell.execute_reply": "2024-10-17T14:55:53.440700Z",
     "shell.execute_reply.started": "2024-10-17T14:55:53.429300Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(60.)"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "c7ab460d-5dcc-427f-9903-ffb756b0e856",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:47:32.337770Z",
     "iopub.status.busy": "2024-10-17T14:47:32.337158Z",
     "iopub.status.idle": "2024-10-17T14:47:32.350888Z",
     "shell.execute_reply": "2024-10-17T14:47:32.348813Z",
     "shell.execute_reply.started": "2024-10-17T14:47:32.337725Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<img src=\"https://pytorch.org/assets/images/computational_graph_backward_pass.png\" width=\"400\"/>"
      ],
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Image(url='https://pytorch.org/assets/images/computational_graph_backward_pass.png', width=400)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "9a61478e-db9a-48ee-9b99-54289baa7fb2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:00:14.655593Z",
     "iopub.status.busy": "2024-10-17T15:00:14.654925Z",
     "iopub.status.idle": "2024-10-17T15:00:14.668462Z",
     "shell.execute_reply": "2024-10-17T15:00:14.666254Z",
     "shell.execute_reply.started": "2024-10-17T15:00:14.655545Z"
    }
   },
   "outputs": [],
   "source": [
    "x = torch.tensor([0.5, 0.75], requires_grad=True)\n",
    "y = torch.log(x[0] * x[1]) * torch.sin(x[1])\n",
    "y.backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "1a3ef09f-f50e-4f75-8f3a-57ae56b61a7d",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:00:16.736086Z",
     "iopub.status.busy": "2024-10-17T15:00:16.735500Z",
     "iopub.status.idle": "2024-10-17T15:00:16.748609Z",
     "shell.execute_reply": "2024-10-17T15:00:16.746685Z",
     "shell.execute_reply.started": "2024-10-17T15:00:16.736041Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1.3633, 0.1912])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d0c3cae-f952-4239-b5dd-76103f8197b9",
   "metadata": {},
   "source": [
    "$$\n",
    "\\begin{split}\n",
    "&y=\\log(x_1\\cdot x_2)\\sin(x_2)\\\\\n",
    "&\\frac{\\partial y}{\\partial x_1}=\\sin(x_2)\\frac1{x_1}\\\\\n",
    "&\\frac{\\partial y}{\\partial x_2}=\\frac1{x_2}\\sin(x_2)+\\log(x_1\\cdot x_2)\\cos(x_2)\n",
    "\\end{split}\n",
    "$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "d0e330c5-21d9-435d-8db0-1d82f99fa1ee",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:04:50.123488Z",
     "iopub.status.busy": "2024-10-17T15:04:50.122834Z",
     "iopub.status.idle": "2024-10-17T15:04:50.137279Z",
     "shell.execute_reply": "2024-10-17T15:04:50.135151Z",
     "shell.execute_reply.started": "2024-10-17T15:04:50.123441Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(1.3633, grad_fn=<DivBackward0>)"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.sin(x[1])/x[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "88438c02-ceab-477b-874e-9603d71eab83",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:05:31.426268Z",
     "iopub.status.busy": "2024-10-17T15:05:31.425605Z",
     "iopub.status.idle": "2024-10-17T15:05:31.451520Z",
     "shell.execute_reply": "2024-10-17T15:05:31.449107Z",
     "shell.execute_reply.started": "2024-10-17T15:05:31.426220Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(0.1912, grad_fn=<AddBackward0>)"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.sin(x[1])/x[1] + torch.log(x[0] * x[1]) * torch.cos(x[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "148dfdc5-d2d5-4d9d-9478-a03f9aec1504",
   "metadata": {},
   "source": [
    "### DAG"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ec58f7b2-daca-4aff-bd98-60e7bd5b43a6",
   "metadata": {},
   "source": [
    "- 前向（forward）是对 dag 的拓扑排序\n",
    "    - 反向（backward）是 reversed topo sort"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "31b34b63-f6ba-4949-97a3-47127afaa7ff",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:37:53.979315Z",
     "iopub.status.busy": "2024-10-19T01:37:53.978654Z",
     "iopub.status.idle": "2024-10-19T01:37:53.993799Z",
     "shell.execute_reply": "2024-10-19T01:37:53.991599Z",
     "shell.execute_reply.started": "2024-10-19T01:37:53.979267Z"
    }
   },
   "outputs": [],
   "source": [
    "class Node:\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.parents = []   # 当前节点的前驱节点\n",
    "        self.children = []  # 当前节点的后继节点\n",
    "\n",
    "    def add_child(self, child_node):\n",
    "        \"\"\"添加后继节点，并在后继节点中添加当前节点为前驱节点\"\"\"\n",
    "        self.children.append(child_node)\n",
    "        child_node.parents.append(self)\n",
    "\n",
    "class Graph:\n",
    "    def __init__(self):\n",
    "        self.nodes = []  # 存储图中的所有节点\n",
    "\n",
    "    def add_node(self, node):\n",
    "        self.nodes.append(node)\n",
    "\n",
    "    def add_nodes(self, node_list):\n",
    "        self.nodes.extend(node_list)\n",
    "\n",
    "    def get_nodes(self):\n",
    "        return self.nodes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "f328c745-ef98-4122-ad60-c3df946f8509",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:41:12.848369Z",
     "iopub.status.busy": "2024-10-19T01:41:12.847720Z",
     "iopub.status.idle": "2024-10-19T01:41:12.860874Z",
     "shell.execute_reply": "2024-10-19T01:41:12.858475Z",
     "shell.execute_reply.started": "2024-10-19T01:41:12.848322Z"
    }
   },
   "outputs": [],
   "source": [
    "x1 = Node('x1')\n",
    "x2 = Node('x2')\n",
    "op1 = Node('op1')\n",
    "op2 = Node('op2')\n",
    "op3 = Node('op3')\n",
    "y1 = Node('y1')\n",
    "y2 = Node('y2')\n",
    "\n",
    "x1.add_child(op1)   # x1 是 op1 的前驱\n",
    "x2.add_child(op2)   # x2 是 op2 的前驱\n",
    "op1.add_child(y1)   # op1 是 y1 的前驱\n",
    "op1.add_child(op3)  # op1 是 op3 的前驱\n",
    "op2.add_child(op3)  # op2 是 op3 的前驱\n",
    "op3.add_child(y2)   # op3 是 y2 的前驱"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "1ea27379-517e-44e1-a2a6-84de90691d3c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:41:15.824621Z",
     "iopub.status.busy": "2024-10-19T01:41:15.823995Z",
     "iopub.status.idle": "2024-10-19T01:41:15.834160Z",
     "shell.execute_reply": "2024-10-19T01:41:15.832160Z",
     "shell.execute_reply.started": "2024-10-19T01:41:15.824574Z"
    }
   },
   "outputs": [],
   "source": [
    "dag = Graph()\n",
    "dag.add_nodes([x1, x2, op1, op2, op3, y1, y2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "f00e675f-fbf1-4553-a091-4195ee38b888",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:41:18.347962Z",
     "iopub.status.busy": "2024-10-19T01:41:18.347358Z",
     "iopub.status.idle": "2024-10-19T01:41:18.363585Z",
     "shell.execute_reply": "2024-10-19T01:41:18.361502Z",
     "shell.execute_reply.started": "2024-10-19T01:41:18.347916Z"
    }
   },
   "outputs": [],
   "source": [
    "def topological_sort(graph):\n",
    "    \"\"\"对给定的 DAG 图进行拓扑排序\"\"\"\n",
    "    from collections import deque\n",
    "\n",
    "    in_degree = {node: 0 for node in graph.get_nodes()}  # 初始化所有节点的入度为 0\n",
    "    for node in graph.get_nodes():\n",
    "        for child in node.children:\n",
    "            in_degree[child] += 1  # 计算每个节点的入度\n",
    "\n",
    "    queue = deque([node for node in graph.get_nodes() if in_degree[node] == 0])  # 入度为 0 的节点队列\n",
    "    order = []\n",
    "\n",
    "    while queue:\n",
    "        current = queue.popleft()\n",
    "        order.append(current)\n",
    "        for child in current.children:\n",
    "            in_degree[child] -= 1  # 移除当前节点的影响\n",
    "            if in_degree[child] == 0:\n",
    "                queue.append(child)\n",
    "\n",
    "    if len(order) != len(graph.get_nodes()):\n",
    "        raise ValueError(\"图中存在循环，无法进行拓扑排序。\")\n",
    "    return order"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "e15e8f67-c399-48f9-8e63-42e67a5fc042",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:41:20.105038Z",
     "iopub.status.busy": "2024-10-19T01:41:20.104470Z",
     "iopub.status.idle": "2024-10-19T01:41:20.116515Z",
     "shell.execute_reply": "2024-10-19T01:41:20.114363Z",
     "shell.execute_reply.started": "2024-10-19T01:41:20.104993Z"
    }
   },
   "outputs": [],
   "source": [
    "from graphviz import Digraph\n",
    "\n",
    "def visualize_dag(dag):\n",
    "    nodes = dag.get_nodes()\n",
    "    dot = Digraph(comment='DAG')\n",
    "\n",
    "    # 添加所有节点到图中\n",
    "    for node in nodes:\n",
    "        dot.node(node.name)\n",
    "\n",
    "    # 添加边（节点之间的依赖关系）\n",
    "    for node in nodes:\n",
    "        for child in node.children:\n",
    "            dot.edge(node.name, child.name)\n",
    "\n",
    "    # 渲染并展示图像\n",
    "    return dot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "c0d4640f-07c5-49da-939a-c6852c53fe1a",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:41:21.845787Z",
     "iopub.status.busy": "2024-10-19T01:41:21.845203Z",
     "iopub.status.idle": "2024-10-19T01:41:21.898345Z",
     "shell.execute_reply": "2024-10-19T01:41:21.896583Z",
     "shell.execute_reply.started": "2024-10-19T01:41:21.845743Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/svg+xml": [
       "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
       "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
       " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
       "<!-- Generated by graphviz version 2.50.0 (0)\n",
       " -->\n",
       "<!-- Pages: 1 -->\n",
       "<svg width=\"134pt\" height=\"260pt\"\n",
       " viewBox=\"0.00 0.00 134.00 260.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
       "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 256)\">\n",
       "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-256 130,-256 130,4 -4,4\"/>\n",
       "<!-- x1 -->\n",
       "<g id=\"node1\" class=\"node\">\n",
       "<title>x1</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-234\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"27\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">x1</text>\n",
       "</g>\n",
       "<!-- op1 -->\n",
       "<g id=\"node3\" class=\"node\">\n",
       "<title>op1</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"27\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">op1</text>\n",
       "</g>\n",
       "<!-- x1&#45;&gt;op1 -->\n",
       "<g id=\"edge1\" class=\"edge\">\n",
       "<title>x1&#45;&gt;op1</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M27,-215.7C27,-207.98 27,-198.71 27,-190.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"30.5,-190.1 27,-180.1 23.5,-190.1 30.5,-190.1\"/>\n",
       "</g>\n",
       "<!-- x2 -->\n",
       "<g id=\"node2\" class=\"node\">\n",
       "<title>x2</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-234\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"99\" y=\"-230.3\" font-family=\"Times,serif\" font-size=\"14.00\">x2</text>\n",
       "</g>\n",
       "<!-- op2 -->\n",
       "<g id=\"node4\" class=\"node\">\n",
       "<title>op2</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-162\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"99\" y=\"-158.3\" font-family=\"Times,serif\" font-size=\"14.00\">op2</text>\n",
       "</g>\n",
       "<!-- x2&#45;&gt;op2 -->\n",
       "<g id=\"edge2\" class=\"edge\">\n",
       "<title>x2&#45;&gt;op2</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M99,-215.7C99,-207.98 99,-198.71 99,-190.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"102.5,-190.1 99,-180.1 95.5,-190.1 102.5,-190.1\"/>\n",
       "</g>\n",
       "<!-- op3 -->\n",
       "<g id=\"node5\" class=\"node\">\n",
       "<title>op3</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"99\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">op3</text>\n",
       "</g>\n",
       "<!-- op1&#45;&gt;op3 -->\n",
       "<g id=\"edge4\" class=\"edge\">\n",
       "<title>op1&#45;&gt;op3</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M41.57,-146.83C51.75,-136.94 65.52,-123.55 77.03,-112.36\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"79.47,-114.87 84.2,-105.38 74.59,-109.85 79.47,-114.87\"/>\n",
       "</g>\n",
       "<!-- y1 -->\n",
       "<g id=\"node6\" class=\"node\">\n",
       "<title>y1</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"27\" cy=\"-90\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"27\" y=\"-86.3\" font-family=\"Times,serif\" font-size=\"14.00\">y1</text>\n",
       "</g>\n",
       "<!-- op1&#45;&gt;y1 -->\n",
       "<g id=\"edge3\" class=\"edge\">\n",
       "<title>op1&#45;&gt;y1</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M27,-143.7C27,-135.98 27,-126.71 27,-118.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"30.5,-118.1 27,-108.1 23.5,-118.1 30.5,-118.1\"/>\n",
       "</g>\n",
       "<!-- op2&#45;&gt;op3 -->\n",
       "<g id=\"edge5\" class=\"edge\">\n",
       "<title>op2&#45;&gt;op3</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M99,-143.7C99,-135.98 99,-126.71 99,-118.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"102.5,-118.1 99,-108.1 95.5,-118.1 102.5,-118.1\"/>\n",
       "</g>\n",
       "<!-- y2 -->\n",
       "<g id=\"node7\" class=\"node\">\n",
       "<title>y2</title>\n",
       "<ellipse fill=\"none\" stroke=\"black\" cx=\"99\" cy=\"-18\" rx=\"27\" ry=\"18\"/>\n",
       "<text text-anchor=\"middle\" x=\"99\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">y2</text>\n",
       "</g>\n",
       "<!-- op3&#45;&gt;y2 -->\n",
       "<g id=\"edge6\" class=\"edge\">\n",
       "<title>op3&#45;&gt;y2</title>\n",
       "<path fill=\"none\" stroke=\"black\" d=\"M99,-71.7C99,-63.98 99,-54.71 99,-46.11\"/>\n",
       "<polygon fill=\"black\" stroke=\"black\" points=\"102.5,-46.1 99,-36.1 95.5,-46.1 102.5,-46.1\"/>\n",
       "</g>\n",
       "</g>\n",
       "</svg>\n"
      ],
      "text/plain": [
       "<graphviz.graphs.Digraph at 0x7bfe8e525780>"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "visualize_dag(dag)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "2812bf7c-38cd-4a66-8d6c-093f164d890b",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:42:00.575146Z",
     "iopub.status.busy": "2024-10-19T01:42:00.574544Z",
     "iopub.status.idle": "2024-10-19T01:42:00.586642Z",
     "shell.execute_reply": "2024-10-19T01:42:00.584775Z",
     "shell.execute_reply.started": "2024-10-19T01:42:00.575102Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x1\n",
      "x2\n",
      "op1\n",
      "op2\n",
      "y1\n",
      "op3\n",
      "y2\n"
     ]
    }
   ],
   "source": [
    "forward_order = topological_sort(dag)\n",
    "for node in forward_order:\n",
    "    print(node.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "70223643-fc78-489f-9010-0bcaf292a639",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-19T01:42:19.159310Z",
     "iopub.status.busy": "2024-10-19T01:42:19.158675Z",
     "iopub.status.idle": "2024-10-19T01:42:19.169100Z",
     "shell.execute_reply": "2024-10-19T01:42:19.167059Z",
     "shell.execute_reply.started": "2024-10-19T01:42:19.159264Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "y2\n",
      "op3\n",
      "y1\n",
      "op2\n",
      "op1\n",
      "x2\n",
      "x1\n"
     ]
    }
   ],
   "source": [
    "backward_order = list(reversed(forward_order))\n",
    "for node in backward_order:\n",
    "    print(node.name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7fb0cde5-3370-44fe-b67f-56c9abe2bc3c",
   "metadata": {},
   "source": [
    "### argmax/argmin：不可微"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "0ea68152-447c-476f-9d19-b33606969088",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:44:52.026128Z",
     "iopub.status.busy": "2024-10-17T14:44:52.025556Z",
     "iopub.status.idle": "2024-10-17T14:44:52.039215Z",
     "shell.execute_reply": "2024-10-17T14:44:52.037094Z",
     "shell.execute_reply.started": "2024-10-17T14:44:52.026083Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([ 0.3367,  0.1288,  0.2345,  0.2303, -1.1229], requires_grad=True)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.randn(5, requires_grad=True)\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "5b6c8eac-2d1f-4e10-b94d-d16aff428253",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:45:50.554671Z",
     "iopub.status.busy": "2024-10-17T14:45:50.554023Z",
     "iopub.status.idle": "2024-10-17T14:45:50.568952Z",
     "shell.execute_reply": "2024-10-17T14:45:50.566853Z",
     "shell.execute_reply.started": "2024-10-17T14:45:50.554624Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([ 0.3367,  0.1288,  0.2345,  0.2303, -1.1229], requires_grad=True)"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "dc728804-7898-4c54-820e-1995d9a7c0a5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:46:00.606468Z",
     "iopub.status.busy": "2024-10-17T14:46:00.605842Z",
     "iopub.status.idle": "2024-10-17T14:46:00.625452Z",
     "shell.execute_reply": "2024-10-17T14:46:00.623242Z",
     "shell.execute_reply.started": "2024-10-17T14:46:00.606422Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(4)"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torch.argmin(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "5c8732f2-8ee3-478e-99b6-0d1d6e22cf15",
   "metadata": {
    "collapsed": true,
    "execution": {
     "iopub.execute_input": "2024-10-17T14:46:16.209283Z",
     "iopub.status.busy": "2024-10-17T14:46:16.208632Z",
     "iopub.status.idle": "2024-10-17T14:46:16.305414Z",
     "shell.execute_reply": "2024-10-17T14:46:16.303005Z",
     "shell.execute_reply.started": "2024-10-17T14:46:16.209236Z"
    },
    "jupyter": {
     "outputs_hidden": true,
     "source_hidden": true
    }
   },
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "element 0 of tensors does not require grad and does not have a grad_fn",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[22], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m y \u001b[38;5;241m=\u001b[39m torch\u001b[38;5;241m.\u001b[39margmax(x)\n\u001b[0;32m----> 2\u001b[0m \u001b[43my\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/anaconda3/lib/python3.10/site-packages/torch/_tensor.py:521\u001b[0m, in \u001b[0;36mTensor.backward\u001b[0;34m(self, gradient, retain_graph, create_graph, inputs)\u001b[0m\n\u001b[1;32m    511\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m has_torch_function_unary(\u001b[38;5;28mself\u001b[39m):\n\u001b[1;32m    512\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m handle_torch_function(\n\u001b[1;32m    513\u001b[0m         Tensor\u001b[38;5;241m.\u001b[39mbackward,\n\u001b[1;32m    514\u001b[0m         (\u001b[38;5;28mself\u001b[39m,),\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m    519\u001b[0m         inputs\u001b[38;5;241m=\u001b[39minputs,\n\u001b[1;32m    520\u001b[0m     )\n\u001b[0;32m--> 521\u001b[0m \u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mautograd\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    522\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgradient\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minputs\u001b[49m\n\u001b[1;32m    523\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/anaconda3/lib/python3.10/site-packages/torch/autograd/__init__.py:289\u001b[0m, in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[0m\n\u001b[1;32m    284\u001b[0m     retain_graph \u001b[38;5;241m=\u001b[39m create_graph\n\u001b[1;32m    286\u001b[0m \u001b[38;5;66;03m# The reason we repeat the same comment below is that\u001b[39;00m\n\u001b[1;32m    287\u001b[0m \u001b[38;5;66;03m# some Python versions print out the first line of a multi-line function\u001b[39;00m\n\u001b[1;32m    288\u001b[0m \u001b[38;5;66;03m# calls in the traceback and some print out the last line\u001b[39;00m\n\u001b[0;32m--> 289\u001b[0m \u001b[43m_engine_run_backward\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    290\u001b[0m \u001b[43m    \u001b[49m\u001b[43mtensors\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    291\u001b[0m \u001b[43m    \u001b[49m\u001b[43mgrad_tensors_\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    292\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretain_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    293\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcreate_graph\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    294\u001b[0m \u001b[43m    \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    295\u001b[0m \u001b[43m    \u001b[49m\u001b[43mallow_unreachable\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m    296\u001b[0m \u001b[43m    \u001b[49m\u001b[43maccumulate_grad\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m    297\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m~/anaconda3/lib/python3.10/site-packages/torch/autograd/graph.py:768\u001b[0m, in \u001b[0;36m_engine_run_backward\u001b[0;34m(t_outputs, *args, **kwargs)\u001b[0m\n\u001b[1;32m    766\u001b[0m     unregister_hooks \u001b[38;5;241m=\u001b[39m _register_logging_hooks_on_whole_graph(t_outputs)\n\u001b[1;32m    767\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 768\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mVariable\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execution_engine\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun_backward\u001b[49m\u001b[43m(\u001b[49m\u001b[43m  \u001b[49m\u001b[38;5;66;43;03m# Calls into the C++ engine to run the backward pass\u001b[39;49;00m\n\u001b[1;32m    769\u001b[0m \u001b[43m        \u001b[49m\u001b[43mt_outputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\n\u001b[1;32m    770\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m  \u001b[38;5;66;03m# Calls into the C++ engine to run the backward pass\u001b[39;00m\n\u001b[1;32m    771\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[1;32m    772\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m attach_logging_hooks:\n",
      "\u001b[0;31mRuntimeError\u001b[0m: element 0 of tensors does not require grad and does not have a grad_fn"
     ]
    }
   ],
   "source": [
    "y = torch.argmax(x)\n",
    "y.backward()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dce06bfb-e837-4678-a4b4-362914457e7e",
   "metadata": {},
   "source": [
    "### topk (看似不可微，实则可微)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "48f6f5c4-8859-4858-a09a-a0278a311e19",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:44:53.801407Z",
     "iopub.status.busy": "2024-10-17T14:44:53.800797Z",
     "iopub.status.idle": "2024-10-17T14:44:53.815243Z",
     "shell.execute_reply": "2024-10-17T14:44:53.813130Z",
     "shell.execute_reply.started": "2024-10-17T14:44:53.801362Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0.3367, 0.2345, 0.2303], grad_fn=<TopkBackward0>)"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "values, indices = torch.topk(x, 3)\n",
    "values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "695319e8-8f42-4381-90c8-b65954866b04",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:44:55.447084Z",
     "iopub.status.busy": "2024-10-17T14:44:55.446510Z",
     "iopub.status.idle": "2024-10-17T14:44:55.456247Z",
     "shell.execute_reply": "2024-10-17T14:44:55.454196Z",
     "shell.execute_reply.started": "2024-10-17T14:44:55.447040Z"
    }
   },
   "outputs": [],
   "source": [
    "values[0].backward(retain_graph=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "38500fff-94f3-4cfc-81cc-286fd6d8cfac",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:44:57.116183Z",
     "iopub.status.busy": "2024-10-17T14:44:57.115596Z",
     "iopub.status.idle": "2024-10-17T14:44:57.129343Z",
     "shell.execute_reply": "2024-10-17T14:44:57.127376Z",
     "shell.execute_reply.started": "2024-10-17T14:44:57.116127Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1., 0., 0., 0., 0.])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "46ceb911-c107-4915-a334-f7ca867a3d88",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:44:59.108158Z",
     "iopub.status.busy": "2024-10-17T14:44:59.107585Z",
     "iopub.status.idle": "2024-10-17T14:44:59.117541Z",
     "shell.execute_reply": "2024-10-17T14:44:59.115260Z",
     "shell.execute_reply.started": "2024-10-17T14:44:59.108114Z"
    }
   },
   "outputs": [],
   "source": [
    "values[1].backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "4eba874a-c0fa-458e-9c93-16efaebddc38",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T14:45:04.271269Z",
     "iopub.status.busy": "2024-10-17T14:45:04.270633Z",
     "iopub.status.idle": "2024-10-17T14:45:04.285290Z",
     "shell.execute_reply": "2024-10-17T14:45:04.282898Z",
     "shell.execute_reply.started": "2024-10-17T14:45:04.271223Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1., 0., 1., 0., 0.])"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "a7af9e23-879e-470a-b6b5-27abacaf0236",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:14:03.531812Z",
     "iopub.status.busy": "2024-10-17T15:14:03.531192Z",
     "iopub.status.idle": "2024-10-17T15:14:03.547217Z",
     "shell.execute_reply": "2024-10-17T15:14:03.545034Z",
     "shell.execute_reply.started": "2024-10-17T15:14:03.531767Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([-0.1863,  2.2082, -0.6380,  0.4617,  0.2674], requires_grad=True)"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.randn(5, requires_grad=True)\n",
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "79032635-7cd7-4806-b6b0-025660bfa0fa",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:14:54.718733Z",
     "iopub.status.busy": "2024-10-17T15:14:54.718096Z",
     "iopub.status.idle": "2024-10-17T15:14:54.742737Z",
     "shell.execute_reply": "2024-10-17T15:14:54.740293Z",
     "shell.execute_reply.started": "2024-10-17T15:14:54.718686Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0., 1., 0., 1., 1.], grad_fn=<WhereBackward0>)"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y = torch.where(x > 0, torch.tensor(1., requires_grad=True), torch.tensor(0., requires_grad=True))\n",
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "c9fcc5bd-24e3-4a39-9359-3285a7d2b44c",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:14:56.653132Z",
     "iopub.status.busy": "2024-10-17T15:14:56.652541Z",
     "iopub.status.idle": "2024-10-17T15:14:56.669159Z",
     "shell.execute_reply": "2024-10-17T15:14:56.666988Z",
     "shell.execute_reply.started": "2024-10-17T15:14:56.653087Z"
    }
   },
   "outputs": [],
   "source": [
    "y.sum().backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "83a0e7f5-609c-4939-abc8-b1e5f7765c19",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:15:00.580264Z",
     "iopub.status.busy": "2024-10-17T15:15:00.579650Z",
     "iopub.status.idle": "2024-10-17T15:15:00.588503Z",
     "shell.execute_reply": "2024-10-17T15:15:00.586636Z",
     "shell.execute_reply.started": "2024-10-17T15:15:00.580219Z"
    }
   },
   "outputs": [],
   "source": [
    "x.grad"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "b5794001-a96b-41c3-965e-a60e9491e596",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:15:48.870791Z",
     "iopub.status.busy": "2024-10-17T15:15:48.870238Z",
     "iopub.status.idle": "2024-10-17T15:15:48.881665Z",
     "shell.execute_reply": "2024-10-17T15:15:48.879694Z",
     "shell.execute_reply.started": "2024-10-17T15:15:48.870752Z"
    }
   },
   "outputs": [],
   "source": [
    "y = torch.where(x > 0, torch.tensor(1., requires_grad=True), torch.tensor(0., requires_grad=True))\n",
    "y[0].backward()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "id": "21cb6f31-c57c-4871-a6c7-67096e0d1dc2",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-10-17T15:15:53.423189Z",
     "iopub.status.busy": "2024-10-17T15:15:53.422538Z",
     "iopub.status.idle": "2024-10-17T15:15:53.432281Z",
     "shell.execute_reply": "2024-10-17T15:15:53.430064Z",
     "shell.execute_reply.started": "2024-10-17T15:15:53.423129Z"
    }
   },
   "outputs": [],
   "source": [
    "x.grad"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
